/* Copyright (C) 2007-2008  klevgert  http://code.google.com/p/db4o-netbeans/

This file is part of the db4o-netbeans module suite.

db4o-netbeans is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o-netbeans is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */

package com.db4o.nb.editor.nodes;

import com.db4o.ObjectContainer;
import com.db4o.ext.ExtObjectContainer;
import com.db4o.ext.ObjectInfo;
import com.db4o.ext.StoredClass;
import com.db4o.nb.util.DialogUtil;
import com.db4o.nb.util.PreferencesUtil;
import com.db4o.nb.util.Updateable;
import com.db4o.reflect.ReflectClass;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import org.openide.ErrorManager;
import org.openide.actions.PropertiesAction;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Index;
import org.openide.nodes.Node;
import org.openide.nodes.Node.Property;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.actions.SystemAction;

/**
 *
 * @author klevgert
 */
public class ClassNode extends AbstractNode implements Updateable {
  
  static final String ICON_PATH = "com/db4o/nb/editor/resources/class.png";

  private ReflectClass reflectClass;
  private ObjectContainer objectContainer;
  private int thresholdFactor = 1;

  /** 
   * Creates a new instance of ClassNode 
   * @param rc    represented db4o class of this node
   * @param oc    db4o database housing the class.
   */
  public ClassNode(ReflectClass rc, ObjectContainer oc) {
     super (new ClassNodeChildren(rc,oc));
    
    this.reflectClass = rc;
    this.objectContainer = oc;
    this.setDisplayName (rc.toString());    
    this.setIconBaseWithExtension(ICON_PATH);
  }

  /**
   * Gets the represented db4o class.
   * @return Represented db4o class.
   */
  public ReflectClass getReflectClass() {
    return this.reflectClass;
  }

  /**
   * Gets the db4o object container housing the db4o class.
   * @return db4o object container providing access to the database
   */
  public ObjectContainer getObjectContainer() {
    return this.objectContainer;
  }
  
  /**
   * Gets a factor how many objects should be fetched  
   * @return threshold factor value.
   */
  public int getThresholdFactor() {
    return this.thresholdFactor;
  }

  /**
   * Sets a factor how many objects should be fetched  
   * @thresholdFactor   threshold factor
   */
  public void setThresholdFactor(int thresholdFactor) {
    this.thresholdFactor = thresholdFactor;
  }

  public void incrementThresholdFactor() {
    this.setThresholdFactor(this.getThresholdFactor() + 1);
  }

  public void reloadChildren() {
    this.setChildren(new ClassNodeChildren(reflectClass,objectContainer));
  }
  
  public void update() {
    this.incrementThresholdFactor();
    this.reloadChildren();
  }
 
  protected Sheet createSheet() {
    
    Sheet sheet = Sheet.createDefault();
    Sheet.Set set = sheet.createPropertiesSet();
    try {
      createInfoProperty(set);
      createCountProperty(set);
        
    } catch (Exception ex) {
        ErrorManager.getDefault().notify (ex);
    }
    sheet.put(set);
    return sheet;
  }

  private void createCountProperty(final Sheet.Set set) {
    
    String displayName = NbBundle.getMessage(this.getClass(), "Count" );
    Property countProp = new PropertySupport.ReadOnly("count", Integer.class, 
            displayName, null) {
      public Object getValue() {
        StoredClass sc = ClassNode.this.objectContainer.ext().storedClass(ClassNode.this.reflectClass.getName());
        return new Integer(sc.getIDs().length);
      }
    };
    countProp.setName("count");
    set.put (countProp);
  }

  private void createInfoProperty(final Sheet.Set set) {

    String displayName = NbBundle.getMessage(this.getClass(), "Info" );
    Property infoProp = new PropertySupport.ReadOnly("info", String.class, 
            displayName, null) {
      public Object getValue() {
        return ClassNode.this.getReflectClass().getName();
      }
    };
    infoProp.setName("info");
    set.put (infoProp);
  }

  public Action[] getActions(boolean popup) {
    return new Action[] {
      new AddObjectAction(this),
      null,
      SystemAction.get(PropertiesAction.class)      
    };
  }    
  
  // <editor-fold defaultstate="collapsed" desc=" Action ">                          

  /**
   * Action that adds a new object.
   */
  public class AddObjectAction extends AbstractAction {
    
    private ClassNode node;
    
    public AddObjectAction(ClassNode node) {

      String displayName = NbBundle.getMessage(this.getClass(), "NewObject" );
      this.putValue(NAME, displayName);
      this.node = node;
    }
    
    public void actionPerformed(ActionEvent e) {

      ExtObjectContainer eoc = ClassNode.this.getObjectContainer().ext();
      Object newInstance = ClassNode.this.reflectClass.newInstance();
      eoc.set(newInstance);
      
      long newOid= eoc.getID(newInstance);
      
      this.node.getChildren().add( new Node[] { new ObjectNode(newOid,eoc) });
    }

  } 
  // </editor-fold>                          

  // <editor-fold defaultstate="collapsed" desc=" Children ">                          

  /**
   *
   * @author klevgert
   */
  public static class ClassNodeChildren extends Index.ArrayChildren {

    protected ReflectClass reflectClass;
    private ObjectContainer objectContainer;
    private int thresholdFactor = 1;

    /** Creates a new instance of ClassChildren */
    public ClassNodeChildren(
      ReflectClass reflectClass, ObjectContainer objectContainer) {

      this.reflectClass = reflectClass;
      this.objectContainer = objectContainer;

    }

    protected java.util.Collection initCollection() {

      // Get the value how many instances should be displayed
      int thrVal = PreferencesUtil.getThresholdValue();
      int thresholdValue = thrVal*((ClassNode)this.getNode()).getThresholdFactor();
      
      ArrayList childrenNodes = new ArrayList();
      try {

        ExtObjectContainer eoc = this.objectContainer.ext();

        StoredClass sc = this.objectContainer.ext().storedClass(this.reflectClass.getName());
        long[] ids = sc.getIDs();
        int cnt = (ids.length < thresholdValue)? ids.length : thresholdValue;

        childrenNodes = new ArrayList(cnt + 1);
        for (int j = 0; j < cnt; j++) {

          ObjectInfo oi = eoc.getObjectInfo(eoc.getByID(ids[j]));
          AbstractNode result = new ObjectNode(ids[j],this.objectContainer);
          childrenNodes.add(result);

          eoc.activate(oi.getObject(),2);
        }

        if( ids.length > thresholdValue ) {
          AbstractNode result = new MoreObjectsNode(ids.length-thresholdValue);
          childrenNodes.add(result);
        }
      
      } catch(com.db4o.ext.Db4oException e) {
        
        ErrorManager.getDefault().log(ErrorManager.ERROR, e.toString());
        String message = NbBundle.getMessage(this.getClass(), "CannotAccessContent", ((e!=null)? e.toString(): "") );
        DialogUtil.showErrorMessage(message);
      }

      return childrenNodes;
    }

  } 
  
  // </editor-fold>                          

}
